V5 to V6
Prerequisites
Angular V20 Migration
Editor's migration guides
Deprecation of Angular's animations package
Angular has deprecated the animations package to use the native CSS instead.
Your project can lead to some warning issues when linting.
To fix them, follow the official migration documentation
Manual migration instructions
For each Angular project :
- Decline
use-application-buildermigration - Decline
control-flow-migrationmigration - Accept
router-current-navigationmigration
EF Migration History table migration
BIA Framework Migration
Run it automatically by clicking on Migrate button
or
Execute each step manually until step 3 - Apply Diff
Mind to check the output logs to check any errors or missing deleted files
-
SOLUTION 1 : Merging rejected files
-
SOLUTION 2 : Analyzing rejected files - MANUAL MIGRATION ONLY
Use the conflict resolution chapter to help you
For each Angular project :
Download the migration script (.txt - Save link as), then :
For each Angular project :
- validate current path as target path
- accept reformat templates option
If some warning about deprecation of NgIf, NgFor or NgSwitch are printed from the final linting, it means that the execution of ng generate @angular/core:control-flow has ignored the migration for concerned file due to some safety constraints.
You have to manually migrate to control flow instructions the identified files (Angular Documentation)
For manual management of conflitcs case : you can remove the .rej files
Conflict Resolution
all-environments.ts
Keep all your teams definition, there will be used by the migration script for the Team Configuration step
AuditFeature.cs
Into AuditTypeMapper method, integrate your old switch case conditions into the new one :
public override Type AuditTypeMapper(Type type)
{
switch(type.Name)
{
case "MyEntity":
return typeof(MyEntityAudit);
default:
return base.AuditTypeMapper(type);
}
}
public override Type AuditTypeMapper(Type type)
{
return type.Name switch
{
// Your previous mapping here
nameof(MyEntity) => typeof(MyEntityAudit),
// BIAToolKit - Begin AuditTypeMapper
// BIAToolKit - End AuditTypeMapper
nameof(User) => typeof(UserAudit),
_ => base.AuditTypeMapper(type),
};
}
CS projects packages references
Keep your packages references into your .csproj, they will be modified by the migration script on the next steps
Front Manual Steps
Full code Index Component
For your full code feature-index.component.html, you will have to add into the .ts the following method :
onViewNameChange(viewName: string | null) {}
Or, you can simply remove from the .html the following binding : (viewNameChange)="onViewNameChange($event)"
Team Configuration
Automatically handled by migration script, for information purpose and manual adjustement only.
From the file all-environments.ts, move the content of your teams configuration into back-end file TeamConfig.cs :
export const allEnvironments = {
teams: [
{
teamTypeId: TeamTypeId.MyTeam,
// Configuration to move below
roleMode: RoleMode.AllRoles,
inHeader: true,
displayOne: false,
displayAlways: false,
teamSelectionCanBeEmpty: false,
label: 'myTeam.headerLabel',
},
]
}
public static class TeamConfig
{
public static readonly ImmutableList<BiaTeamConfig<BaseEntityTeam>> Config = new ImmutableListBuilder<BiaTeamConfig<BaseEntityTeam>>()
{
new BiaTeamConfig<BaseEntityTeam>()
{
TeamTypeId = (int)TeamTypeId.MyTeam,
RightPrefix = "MyTeam",
AdminRoleIds = [(int)RoleId.MyTeamAdmin],
TeamAutomaticSelectionMode = BIA.Net.Core.Common.Enum.TeamSelectionMode.None,
// Configuration from TS
RoleMode = BIA.Net.Core.Common.Enum.RoleMode.AllRoles,
DisplayInHeader = true,
DisplayOne = false,
DisplayAlways = false,
TeamSelectionCanBeEmpty = false
Label = "myTeam.headerLabel",
},
}
}
Then, remove the teams from all-environments.ts
useRefreshAtLanguageChange
In previous version, you'll have to use useRefreshAtLanguageChange property defined into your index components, usually to handle changes of current culture to refresh your data by calling onLoadLazy :
ngOnInit(): void {
super.ngOnInit();
if (this.useRefreshAtLanguageChange) {
this.sub.add(
this.biaTranslationService.currentCulture$
.pipe(skip(1))
.subscribe(() => {
this.onLoadLazy(this.crudItemListComponent.getLazyLoadMetadata());
})
);
}
}
By now, this refresh is automatically handled into the CrudItemsIndexCompoent by using the property useRefreshAtLanguageChange from the crudConfiguration.
So, you can remove from your index component the handler of this.biaTranslationService.currentCulture$ to call onLoadLazy, and set into your feature constants the useRefreshAtLanguageChange to true :
export const announcementCRUDConfiguration: CrudConfig<MyFeature> =
new CrudConfig({
// [...]
useRefreshAtLanguageChange: true,
});
For all cases using the previous useRefreshAtLanguageChange from CrudItemIndexComponent, simply use now the same property from the crudConfiguration.
Back Manual Steps
Package Version
Open the DotNet\Directory.Packages.Project.props file and check that no package version contains a wildcard (*). If a wildcard is found, replace it with the desired version number.
Example Before
<Project>
<ItemGroup>
<!-- Add the versions of your packages here. -->
<!-- <PackageVersion Include="MyPackageNameExample" Version="6.0.4" /> -->
<PackageVersion Include="Microsoft.AspNetCore.Http.Features" Version="2.2.0" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.*" />
</ItemGroup>
</Project>
Example After
<Project>
<ItemGroup>
<!-- Add the versions of your packages here. -->
<!-- <PackageVersion Include="MyPackageNameExample" Version="6.0.4" /> -->
<PackageVersion Include="Microsoft.AspNetCore.Http.Features" Version="2.2.0" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
</ItemGroup>
</Project>
Audit Entities
For all your previous audit entities inherited from AuditEntity :
- inherits from
AuditKeyedEntity<TEntity, TEntityKey, TAuditKey>for audited entities with single PK - inherits from
AuditEntity<TEntity, TAuditKey>for audited join entities with composite PK
See Audit documentation for dedicated audit tables.
OperationalDomainServiceBase inheritage and override
Inheritage
You must fix your class that inherits from OperationalDomainServiceBase by providing new required generic types :
TDto: DTO mapped to yourTEntityTDtoListItem: DTO mapped to yourTEntityfor your lists display and exportTMapper: mapper between your entity and the DTOTMapperListItem: mapper between your entity and the DTO used for listsTFilterDto: filter type of your entity
Prefer to inherit from CrudAppServiceBase or CrudAppServiceListAndItemBase when you need the OperationDomainServiceBase methods.
Inherit directly from DomainServiceBase if you just need to access to the IRepository.
public class MyService : OperationDomainServiceBase<MyEntity, int>
public class MyService : OperationDomainServiceBase<MyEntityDto, MyEntityDto, MyEntity, int, PagingFilterFormatDto, MyEntityMapper, MyEntityMapper>
You can inherits your class interface from the dedicated interface IOperationDomainServiceBase<TDto, TDtoListItem, TEntity, TKey, TFilterDto> if needed
Overrides
- These methods have been renamed as
{MethodName}GenericAsync:GetRangeAsync<TOtherDto, TOtherMapper, TOtherFilterDto>GetAllAsync<TOtherDto, TOtherMapper>GetCsvAsync<TOtherDto, TOtherMapper, TOtherFilterDto>GetAsync<TOtherDto, TOtherMapper>AddAsync<TOtherDto, TOtherMapper>UpdateAsync<TOtherDto, TOtherMapper>RemoveAsync<TOtherDto, TOtherMapper>SaveSafeAsync<TOtherDto, TOtherMapper>SaveAsync<TOtherDto, TOtherMapper>UpdateFixedAsync<TOtherDto, TOtherMapper>
You should only override the public non generic methods equivalent
public class MyService : OperationDomainServiceBase<MyEntity, int>
{
protected override async Task<(IEnumerable<TOtherDto> Results, int Total)> GetRangeAsync<TOtherDto, TOtherMapper, TOtherFilterDto>(TOtherFilterDto filters = null, int id = default, Specification<MyEntity> specification = null, Expression<Func<MyEntity, bool>> filter = null, string accessMode = "Read", string queryMode = "ReadList", string mapperMode = null, bool isReadOnlyMode = false)
{
// Custom code...
return await base.GetRangeAsync<TOtherDto, TOtherMapper, TOtherFilterDto>(filters, id, specification, filter, accessMode, queryMode, mapperMode, isReadOnlyMode);
}
}
public class MyService : OperationDomainServiceBase<MyEntityDto, MyEntityDto, MyEntity, int, PagingFilterFormatDto, MyEntityMapper, MyEntityMapper>
{
public override async Task<(IEnumerable<MyEntityDto> Results, int Total)> GetRangeAsync(PagingFilterFormatDto filters = null, int id = 0, Specification<MyEntity> specification = null, Expression<Func<MyEntity, bool>> filter = null, string accessMode = "Read", string queryMode = "ReadList", string mapperMode = null, bool isReadOnlyMode = false)
{
// Custom code...
return await base.GetRangeAsync(filters, id, specification, filter, accessMode, queryMode, mapperMode, isReadOnlyMode);
}
}
The transformation for the previous overrides of the protected generic methods into public non generic, and the rename of the generic methods usage, are automatically handled by migration script for classes that inherits from CrudAppServiceBase or CrudAppServiceListAndItemBase.
Please review the migration script actions into your application services.
- Following methods required now one or multiple delegate parameter in order to execute their algorithm with overriden methods :
GetCsvGenericAsync<TOtherDto, TOtherMapper, TOtherFilterDto>SaveSafeGenericAsync<TOtherDto, TOtherMapper>SaveGenericAsync<TOtherDto, TOtherMapper>UpdateFixedGenericAsync<TOtherDto, TOtherMapper>
Example below shows you how to call properly one of them with the correct delegate :
public class MyService : OperationDomainServiceBase<MyEntityDto, MyEntityDto, MyEntity, int, PagingFilterFormatDto, MyEntityMapper, MyEntityMapper>
{
public Task<byte[]> GetCsvCustomAsync()
{
// Use of non generic GetRangeAsync
return this.GetCsvGenericAsync<MyEntityDto, MyEntityMapper, PagingFilterFormatDto>(this.GetRangeAsync);
// Use of generic GetRangeGenericAsync with custom types
return this.GetCsvGenericAsync<MyOtherEntityDto, MyOtherEntityMapper, CustomPagingFilterFormatDto>(this.GetRangeGenericAsync<MyOtherEntityDto, MyOtherEntityMapper, CustomPagingFilterFormatDto>);
// Use of custom generic GetRangeGenericAsync with custom types
return this.GetCsvGenericAsync<MyOtherEntityDto, MyOtherEntityMapper, CustomPagingFilterFormatDto>(this.GetRangeGenericCustomAsync<MyOtherEntityDto, MyOtherEntityMapper, CustomPagingFilterFormatDto>);
}
// Must respect the signature of the delegate GetRangeGenericAsyncDelegate
private async Task<(IEnumerable<TOtherDto> Results, int Total)> GetRangeGenericCustomAsync<TOtherDto, TOtherMapper, TOtherFilterDto>(TOtherFilterDto filters = null, Guid id = default, Specification<Pilot> specification = null, Expression<Func<Pilot, bool>> filter = null, string accessMode = "Read", string queryMode = "ReadList", string mapperMode = null, bool isReadOnlyMode = false)
{
// Custom code...
}
}
:::
Database Migration
You must create a new database migration in order to apply framework changes to your database scheme :
add-migration MigrationBiaFrameworkV6 -c datacontextupdate-database -context datacontext
Build Pipeline
-
Set the Visual Studio version
- Task: Build solution → Visual Studio Version
- Value: Visual Studio 2026
-
Declare pipeline variables
- Name:
DotNetVersion - Value:
net10.0
- Name:
-
Update paths to target
.NET 10.0- API Tests → Test files
**\$(BuildConfiguration)\$(DotNetVersion)\*$(ProjectName).Test.dll
!**\obj\** - Copy Files Presentation Api → Source Folder
DotNet/$(CompanyName).$(ProjectName).Presentation.Api/bin/$(BuildConfiguration)/$(DotNetVersion) - Copy Files Worker service → Source Folder
DotNet/$(CompanyName).$(ProjectName).WorkerService/bin/$(BuildConfiguration)/$(DotNetVersion) - Copy Files DeployDB → Source Folder
DotNet/$(CompanyName).$(ProjectName).DeployDB/bin/$(BuildConfiguration)/$(DotNetVersion)
- API Tests → Test files